(function () {
    const PREFIX = 'tk-';

    //图片裁剪
    let VUCropper = `
	<div class="vu-cropper" :style="dimensions">
		<div class="cropper-header" :style="lineHeight">
			<div class="cropper-thumbnail">
				<img v-if="value" :src="value" :style="dimensions" alt="">
				<i v-else class="el-icon-picture-outline cropper-default" :style="defaultBorder"></i>
			</div>
			<div class="vu-cropper-modal" v-if="editable">
				<i class="el-icon-edit-outline vu-icon" @click="$refs.file.click()"></i>
				<i v-if="value && value!=defaultImage" class="el-icon-delete vu-icon" @click="remove"></i>
			</div>	
			<input type="file" class="copper-file" @change="tailor" ref="file" :accept="accept">
		</div>
		<el-dialog title="文件上传" :visible.sync="dialogVisible" :append-to-body="appendToBody" :close-on-click-modal="false" :before-close="close">
			<div class="cropper-body" :style="theight">
				<div class="cropper-item">
					<img v-show="cropper.source" :src="cropper.source" ref="tailor" class="cropper-image cropper-source" />
					<i v-show="!cropper.source" class="el-icon-picture-outline cropper-notice" />
				</div>
				<div class="cropper-item" style="text-align: center;">
					<img v-show="cropper.preview" ref="previewImg" :style="previewStyle" :src="cropper.preview" class="cropper-image cropper-preview" />
					<span v-show="!cropper.preview" class="cropper-notice">裁剪预览</span>
				</div>
			</div>
			<div class="cropper-footer" v-if="progress>0">
				<el-progress :percentage="progress" class="cropper-progress"></el-progress>
			</div>
			<span slot="footer" class="dialog-footer">
				<el-button :size="size" @click="close">取 消</el-button>
				<el-button :size="size" type="primary" @click="upload" :class="disabled ? 'el-icon-uploading' : ''" :disabled="disabled">上 传</el-button>
			</span>
		</el-dialog>
	</div>
	`;
    Vue.component('vu-cropper', {
        template: VUCropper,
        props: {
            value: String,
            cdnUrl: {
                type: String,
                default: cdnUrl || ''
            },
            //上传尺寸限制 单位KB
            limit: {
                type: Number,
                default: 0
            },
            editable: {
                type: Boolean,
                default: true
            },
            defaultImage: "",
            width: {
                type: String,
                default: '600px'
            },
            height: {
                type: String,
                default: '300px'
            },
            cropperSize: {
                type: Object,
                default() {
                    return {
                        width: 300,
                        height: 500
                    };
                }
            },
            //是否追加
            appendToBody: {
                type: Boolean,
                default: false
            },
            //按钮尺寸
            size: {
                type: String,
                default: 'small'
            },
            //允许接收的文件类型
            accept: {
                type: String,
                default: '.jpeg,.jpg,.png,.gif'
            },
            data: {
                default: null
            }
        },
        data() {
            return {
                disabled: true,
                dialogVisible: false,
                //裁剪器
                cropper: {
                    //裁剪器
                    handler: null,
                    //裁剪源
                    source: null,
                    //预览源
                    preview: null
                },
                progress: 0,
                previewStyle: ''
            };
        },
        watch: {
            'cropper.source': function () {
                this.disabled = !this.cropper.source;
            },
            'cropper.preview': function () {
                let width = parseInt(this.width),
                    height = parseInt(this.height);
                if (width > height) {
                    this.previewStyle = 'width:100%';
                } else {
                    this.previewStyle = 'height:100%;';
                }
            },
        },
        computed: {
            //组件尺寸
            dimensions: function () {
                return 'width:' + this.width + ';height:' + this.height + ';';
            },
            //组件行高
            lineHeight: function () {
                return 'line-height:' + this.height + ';';
            },
            //真实高度
            theight: function () {
                return 'height:' + this.height + ';';
            },
            //默认边框
            defaultBorder: function () {
                return 'width:' + (parseInt(this.width) - 2) + 'px;height:' + (parseInt(this.height) - 2) + 'px;';
            },
            percentX: function () {
                return parseInt(this.cropperSize.width);
            },
            percentY: function () {
                return parseInt(this.cropperSize.height);
            }
        },
        methods: {
            //显示裁剪信息
            tailor(event) {
                let files = event.target.files,
                    $this = this,
                    file;

                //计算文件信息
                if (this.limit > 0 && Math.floor(files[0].size / 1024) > this.limit) {
                    this.$refs.file.value = null;
                    return this.$message.error('抱歉,上传文件大小超出限制');
                }

                this.dialogVisible = true;
                this.$nextTick(() => {
                    let tailor = this.$refs.tailor;

                    if (files && files.length > 0) {
                        file = URL.createObjectURL(files[0]);
                        tailor.src = file;
                        this.cropper.source = file;
                        //销毁已经存在的裁剪器
                        this.cropper.handler && this.cropper.handler.destroy();
                    }

                    this.cropper.handler = new Cropper(tailor, {
                        aspectRatio: $this.percentX / $this.percentY,
                        viewMode: 1,
                        autoCropArea: 1,
                        background: true, //是否显示网格背景
                        guides: true, //是否显示裁剪框虚线
                        zoomable: false,
                        crop() {
                            //这种实时裁剪方式会导致拖拽选取操作卡顿
                            let canvas = $this.cropper.handler.getCroppedCanvas({
                                // maxWidth: $this.percentX * 10,
                                // maxHeight: $this.percentY * 10,
                                imageSmoothingQuality: 'high'
                            });

                            //jpeg自动填充色为黑色 导致出现黑边
                            $this.cropper.preview = canvas.toDataURL("image/png", 1);
                        }
                    });
                });
            },
            //唯一编码
            uniqid() {
                let d = new Date().getTime(),
                    uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                        var r = (d + Math.random() * 16) % 16 | 0;
                        d = Math.floor(d / 16);
                        return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
                    });
                return uuid;
            },
            //数据转换
            dataURLtoFile(data, filename) {
                let arr = data.split(','), mime = arr[0].match(/:(.*?);/)[1],
                    bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
                while (n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }
                return new File([u8arr], filename, {type: mime});
            },
            //上传图片
            upload() {
                this.disabled = true;
                let date = new Date(),
                    month = date.getMonth() + 1,
                    year = date.getFullYear(),
                    filename = this.module + 'images/' + year + '/' + (month < 10 ? '0' : '') + month + '/' + this.uniqid() + '.jpg',
                    file = this.dataURLtoFile(this.cropper.preview, filename),
                    $this = this;
                this.getOssData().then((data) => {
                    let ossClient = $this.getOssClient(data);
                    $this.ossMultiUpload(ossClient, filename, file, 0, {}, function (progress) {
                        //进度条
                        $this.progress = Math.floor(progress * 100);
                    }, function (r) {
                        $this.disabled = false;
                        if (r.res.status !== 200) {
                            return $this.$message.error('上传失败,请联系管理员进行处理');
                        }
                        $this.$refs.file.value = null;
                        $this.dialogVisible = false;
                        $this.progress = 0;
                        console.log(cdnUrl + r.name);
                        $this.$emit('input', cdnUrl + r.name);
                        if (typeof $this.$listeners.submit === 'function') {
                            console.log('sssssssssssssssssssssssssssssssssubmit');
                            $this.$emit('submit', $this.callback, $this.data)
                        }
                    });
                });

            },
            callback(fn) {
                if (!fn || !fn.then) {
                    return '';
                }
                this.loading = true;
                fn.then(() => {
                    this.loading = false;
                }).catch(r => {
                    this.loading = false;
                    console.log(r);
                });
            },
            //关闭窗口
            close(done) {
                this.$refs.file.value = null;
                if (typeof done === 'function') {
                    done();
                } else {
                    this.dialogVisible = false;
                }
            },
            remove() {
                this.$emit('input', this.defaultImage)
                if (this.$listeners.clean) {
                    this.$emit('clean');
                }
            }
        }
    });

    //标签系列组件
    let VUTag = `<span :class="['vu-tag', renderType]" @click="$emit('click')"><slot></slot></span>`;
    Vue.component('vu-tag', {
        props: {
            type: {
                type: String,
                default: 'primary'
            }
        },
        template: VUTag,
        computed: {
            renderType: function () {
                return 'vu-tag-' + this.type;
            }
        }
    });

    //提示类
    let VUNotice = `<p :class="['vu-notice', renderType]" @click="$emit('click')"><span class="vu-notice-danger" v-if="required">*</span><slot></slot></p>`;
    Vue.component('vu-notice', {
        props: {
            type: {
                type: String,
                default: 'comment'
            },
            required: {
                type: Boolean,
                default: false
            }
        },
        template: VUNotice,
        computed: {
            renderType: function () {
                return 'vu-notice-' + this.type;
            }
        }
    });

    //直传
    let VUUpload = `
		<div class="vu-upload" :style="dimensions">
			<div class="vu-upload-icon vu-upload-preview" v-if="value">
				<img @click="$refs.file.click()" :src="value" class="vu-upload-icon" alt="">
				<i class="el-icon-error" @click="remove"></i>
			</div>
			<i @click="$refs.file.click()" class="el-icon-upload vu-upload-icon" v-if="!value"></i>
			<el-progress :percentage="percentage" v-if="showProgress"></el-progress>
			<input type="file" class="hide" ref="file" @change="upload" :accept="accept">
		</div>
	`;
    Vue.component('vu-upload', {
        template: VUUpload,
        props: {
            value: String,
            //显示宽度
            width: {
                type: String,
                default: '600px',
            },
            //显示区域高度
            height: {
                type: String,
                default: '300px',
            },
            //上传格式限制
            accept: {
                type: String,
                default: '.jpg,.png'
            },
            //文件大小限制
            size: {
                type: Number,
                default: 200 //单位 KB
            },
            limitSize: {
                type: Boolean,
                default: true
            }
        },
        data() {
            return {
                action: '', //上传地址
                showProgress: false, //是否显示进度条
                percentage: 0, //上传进度
                file: null, //需要上传的文件信息
            }
        },
        computed: {
            dimensions: function () {
                return 'width:' + this.width + ';height:' + this.height + ';';
            }
        },
        methods: {
            //唯一编码
            uniqid() {
                let d = new Date().getTime(),
                    uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                        var r = (d + Math.random() * 16) % 16 | 0;
                        d = Math.floor(d / 16);
                        return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
                    });
                return uuid;
            },
            //校验
            validator() {
                if (this.limitSize && parseInt(this.file.size / 1024) > this.size) {
                    this.notice('文件大小超过200Kb', 'error');
                    return false;
                }
                return true;
            },
            upload(e) {
                this.file = e.target.files[0];
                let date = new Date(),
                    month = date.getMonth() + 1,
                    year = date.getFullYear(),
                    filename = 'images/' + year + '/' + month + '/' + this.uniqid() + '.jpg',
                    $this = this;
                if (!this.validator()) {
                    return;
                }

                this.getOssData().then((data) => {
                    let ossClient = $this.getOssClient(data);
                    $this.showProgress = true;
                    $this.ossMultiUpload(ossClient, filename, this.file, 0, null, function (progress) {
                        $this.percentage = Math.floor(progress * 100);
                    }, function (r) {
                        if (r.res.statusCode !== 200) {
                            $this.$message.error('上传失败,请联系管理员进行处理');
                        }
                        $this.showProgress = false;
                        $this.$emit('input', cdnUrl + filename);
                    })
                });
            },
            //删除图片
            remove() {
                this.$refs.file.value = null;
                this.$emit('input', '');
            },
        }
    });

    /**
     * 多媒体上传
     */
    let VUMedia = `
	<div class="vu-media" :style="renderStyle" ref="player">
		<div class="media-header">
			<input type="file" class="media-file" ref="mediaFile" @change="pitch" :accept="accept">
			<span v-if="!value.url" class="media-default">惠众物联新媒体</span>
			<div v-else class="media-material">
				<video v-show="upload.type=='video'" loop="loop" :height="playerHeight" :width="playerWidth" ref="media" @canplaythrough="canplay" @timeupdate="playing" @ended="ended" :src="value.url">
					<source :src="value.url" />
				</video>
				<vu-follower v-show="upload.type=='audio'" :width="width" :height="height"></vu-follower>
			</div>
			<div class="media-modal">
				<el-slider :disabled="totalTime<=0" :format-tooltip="format" class="media-progress" v-model="playProgress" @change="change" @mousedown.native="mousedown"></el-slider>
				<div class="media-action">
					<div class="media-item">
						<i v-if="mediaPaused" class="icon-play vu-icon" @click="play"></i>
						<i v-else class="icon-pause vu-icon" @click="pause"></i>
						<i class="media-time"><span>{{ progress }}</span>/{{ duration }}</i>
						<span>&nbsp;&nbsp;&nbsp;&nbsp;技术支持：{{channel==='TX' ? '腾讯云' : '阿里云'}}</span>
					</div>
                    <div class="media-item">
                        <span class="vu-icon" @click="$refs.mediaFile.click()"><i class="el-icon-upload"></i>选择文件</span>
                        <template v-if="value.url">
                            <i class="el-icon-delete vu-icon" @click="remove" v-if="allowDelete"></i>
                            <i class="el-icon-full-screen vu-icon" @click="fullscreen"></i>
                        </template>					
					</div>
				</div>
			</div>
		</div>
		<template v-if="dialog.show">
		<el-dialog title="多媒体文件上传" :visible.sync="dialog.show" width="600px" :close-on-click-modal="false" :before-close="close" :append-to-body="appendToBody">
			<div class="media-body">
				<video v-show="upload.type=='video'" @loadeddata="snapshot" controls loop="loop" height="100%" width="100%" ref="video" @canplaythrough="setDuration">
					<source ref="videoSource" src="#">
				</video>
				<audio v-show="upload.type=='audio'" controls width="100%" ref="audio">
					<source ref="audioSource" src="#">
				</audio>
			</div>
			<el-progress class="vu-progress" v-if="dialog.progress>0" :percentage="dialog.progress" color="#8e71c7"></el-progress>
			<span slot="footer" class="dialog-footer">
				<el-button @click="close" :size="size">取 消</el-button>
				<el-button type="primary" :size="size" @click="upload_file" :disabled="uploading"><i class="el-icon-loading" v-if="uploading"></i>上 传</el-button>
			</span>
		</el-dialog>
		</template>
	</div>`;
    Vue.component('vu-media', {
        template: VUMedia,
        props: {
            value: {
                type: Object,
                default() {
                    return {
                        url: '',
                        size: 0, //多媒体大小 单位MB
                        type: '', //多媒体类别 video / audio
                        mime: '', //多媒体媒介分类 video/mp4 audio/mp3
                        thumbnail: '', //视频封面
                        duration: '', //多媒体时长 单位s,
                        fileId: '', //腾讯视频文件ID
                    }
                }
            },
            width: {
                type: String,
                default: '600px'
            },
            height: {
                type: String,
                default: '300px'
            },
            //是否追加
            appendToBody: {
                type: Boolean,
                default: false
            },
            //是否允许短视频
            allowShortVideo: {
                type: Boolean,
                default: true
            },
            //是否允许删除
            allowDelete: {
                type: Boolean,
                default: true
            },
            //播放器上传线路
            channel: {
                validator: function (value) {
                    return ['AL', 'TX'].indexOf(value) !== -1;
                }
            },
            //按钮大小
            size: {
                type: String,
                default: 'small'
            },
            accept: {
                type: String,
                default: '.mp3,.mp4'
            }
        },
        data() {
            return {
                dialog: {
                    show: false,
                    progress: 0 //上传进度
                },
                mediaPaused: true,
                //当前时间点
                totalTime: 0,
                //播放进度
                playProgress: 0,
                //播放进度时间
                progress: '00:00',
                //总时间
                duration: '00:00',
                fullscreenPlaying: false,
                file: null, //需要上传的文件【二进制格式】
                upload: {
                    url: '',
                    size: 0, //文件大小
                    type: '', //视频类别 video / audio
                    mime: '', //视频媒体分类 video/mp4 audio/mp3
                    thumbnail: '', //视频封面图片,
                    duration: 0, //文件时长
                    suffix: '', //文件后缀名 .mp4 .mp3,
                    fileId: '', //腾讯视频ID
                },
                uploading: false,
                cancelUpload: false,
                playerWidth: 0, //播放器宽度
                playerHeight: 0, //播放器高度,
                timer: 0, //定时器
                thumbnail: null, //封面图base64源码
                vod: null, //腾讯云
                uploader: null, //腾讯云上传句柄
            };
        },
        computed: {
            counter: function () {
                return this.progress + '/' + this.duration;
            },
            renderStyle: function () {
                return 'width:' + this.width + ';height:' + this.height + ';';
            }
        },
        watch: {
            'value.type': function (ov) {
                this.upload.type = ov;
            },
            'value.thumbnail': function (ov) {
                this.upload.thumbnail = ov;
            }
        },
        created() {
            this.init();
        },
        methods: {
            init() {
                this.upload.type = this.value.type;
                let width = this.width,
                    height = this.height,
                    playerWidth = parseInt(width),
                    playerHeight = parseInt(height),
                    widthUnit = width.replace(playerWidth),
                    heightUnit = height.replace(playerHeight);
                this.playerWidth = widthUnit === 'px' ? playerWidth : this.width;
                this.playerHeight = heightUnit === 'px' ? playerHeight : this.height;
                window.addEventListener('resize', () => {
                    if (this.isFullScreen) {
                        this.exitFullScreen();
                    }
                });
            },
            //选择文件
            pitch(event) {
                this.file = event.target.files[0];
                let media_url = URL.createObjectURL(this.file),
                    mime = this.file.type.split('/'), // video/mp4 audio/mp3 audio/mpeg
                    source = mime[0] + 'Source',
                    accept = this.accept.split(',').map((v) => v.substr(1)),
                    suffix = this.file.name.split('.').pop();
                if (accept.indexOf(suffix) === -1) {
                    this.$refs['mediaFile'].value = '';
                    return this.notice('抱歉,不支持的多媒体类型', 'error');
                }

                //上传modal
                this.dialog.show = true;

                this.$nextTick(() => {
                    this.upload.type = mime[0];
                    this.upload.size = (this.file.size / 1048576).toFixed(2);
                    this.upload.mime = this.file.type;
                    this.upload.suffix = '.' + suffix;

                    //选择文件后暂停当前播放的视频
                    if (this.$refs.media) {
                        this.pause();
                    }

                    //清除历史加载
                    this.$refs.video.src = '';
                    this.$refs['videoSource'].src = '';
                    this.$refs.audio.src = '';
                    this.$refs['audioSource'].src = '';
                    this.thumbnail = null;
                    this.cancelUpload = false;

                    //上传进度条
                    this.dialog.progress = 0;

                    this.$refs[mime[0]].src = media_url;
                    this.$refs[source].src = media_url;
                });
            },
            //弹窗video加载完成提取截图
            snapshot() {
                if (this.timer) {
                    clearTimeout(this.timer);
                }
                //延时 100ms 等待视频画面出现
                this.timer = setTimeout(() => {
                    let canvas = document.createElement("canvas");

                    canvas.width = this.$refs.video.videoWidth;
                    canvas.height = this.$refs.video.videoHeight;
                    canvas.getContext('2d').drawImage(this.$refs.video, 0, 0, canvas.width, canvas.height);

                    this.thumbnail = canvas.toDataURL("image/jpeg");
                }, 100);
            },
            //播放
            play() {
                if (!this.$refs.media) {
                    return;
                }
                this.$refs.media.play();
                this.mediaPaused = false;
            },
            //暂停
            pause() {
                this.$refs.media.pause();
                this.mediaPaused = true;
            },
            //外部视频加载完毕
            canplay(event) {
                this.$nextTick(() => {
                    let duration = this.upload.duration ? this.upload.duration : event.target.duration;
                    this.totalTime = Math.floor(duration);
                    this.duration = this.computedProgress(this.totalTime);
                    this.upload.duration = duration;
                });
            },
            setDuration(event) {
                this.$nextTick(() => {
                    if (this.allowShortVideo === false && event.target.duration < 60) {
                        this.close();
                        return this.notice('抱歉,视频时长不足1分钟,请重新选择', 'error');
                    }
                    this.upload.duration = Math.floor(event.target.duration);
                });
            },
            //播放状态
            playing() {
                let total = Math.floor(this.$refs.media.currentTime);
                this.progress = this.computedProgress(total);
                this.playProgress = Math.floor(total / this.totalTime * 100);
            },
            //播放结束
            ended() {
                this.mediaPaused = true;
            },
            //选择播放时间点
            change(percent) {
                //计算时间点
                this.$refs.media.currentTime = percent * this.totalTime / 100;
                this.mediaPaused = false;
                this.$refs.media.play();
            },
            //格式化提示信息
            format(percent) {
                return this.computedProgress(Math.floor(percent * this.totalTime / 100));
            },
            mousedown() {
                if (!this.$refs.media) {
                    return;
                }
                this.mediaPaused = true;
                this.$refs.media.pause();
            },
            //全屏
            fullscreen() {
                if (!this.$refs.media) {
                    return;
                }
                this.isFullScreen = true;

                if (typeof this.$refs.media.requestFullscreen === 'function') {
                    return this.$refs.media.requestFullscreen();
                }

                if (typeof this.$refs.media.mozRequestFullScreen === 'function') {
                    return this.$refs.media.mozRequestFullScreen();
                }

                if (typeof this.$refs.media.msRequestFullscreen === 'function') {
                    return this.$refs.media.msRequestFullscreen();
                }

                if (typeof this.$refs.media.oRequestFullscreen === 'function') {
                    return this.$refs.media.oRequestFullscreen();
                }

                if (typeof this.$refs.media.webkitRequestFullScreen === 'function') {
                    return this.$refs.media.webkitRequestFullScreen();
                } else {
                    this.$message.error('抱歉,您的浏览器暂未支持全屏,请联系管理员进行处理');
                }
            },
            //退出全屏
            exitFullScreen() {
                this.isFullScreen = false;
                this.playerWidth = parseInt(this.width);
                this.playerHeight = parseInt(this.height);
            },
            //关闭窗口
            close(done) {
                this.$refs.video.src = '';
                this.$refs['videoSource'].src = '';
                this.$refs.audio.src = '';
                this.$refs['audioSource'].src = '';
                this.$refs['mediaFile'].value = '';
                this.dialog.progress = 0;
                this.uploading = false;
                this.cancelUpload = true;

                //取消腾讯云上传 已经上传成功的不能取消
                if (this.channel === 'TX' && this.uploader) {
                    this.uploader.cancel();
                }

                if (typeof done === 'function') {
                    done();
                } else {
                    this.dialog.show = false;
                }
            },
            //计算时间
            computedProgress(total) {
                let second = total % 60,
                    minutes = Math.floor((total - second) / 60),
                    minute = minutes % 60,
                    hours = Math.floor((total - second - minute * 60) / 60),
                    hour = hours / 60;

                let _hour = hour > 9 ? hour : '0' + hour,
                    _minute = minute > 9 ? minute : '0' + minute,
                    _second = second > 9 ? second : '0' + second;

                return (hour > 0 ? _hour + ':' : '') + _minute + ':' + _second;
            },
            //唯一编码
            uniqid() {
                let d = new Date().getTime();
                return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    var r = (d + Math.random() * 16) % 16 | 0;
                    d = Math.floor(d / 16);
                    return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
                });
            },
            //上传文件
            upload_file() {
                let date = new Date(),
                    month = date.getMonth() + 1,
                    year = date.getFullYear(),
                    filename = 'media/' + year + '/' + (month < 10 ? '0' : '') + month + '/' + this.uniqid();
                this.channel === 'TX' ? this.tencent(filename) : this.aliyun_upload(filename);
            },
            //腾讯云上传
            tencent(filename) {
                this.uploading = true;
                let vod = new TcVod.default({
                        getSignature: this.getTencentToken
                    }),
                    uploaded = false;
                this.uploader = vod.upload({mediaFile: this.file});

                this.uploader.on('media_progress', r => {
                    this.dialog.progress = parseInt(r.percent * 100);
                });

                this.uploader.on('media_upload', function (r) {
                    uploaded = true;
                });

                this.uploader.done().then(r => {
                    this.upload.fileId = r.fileId;
                    this.upload.url = r.video.url;
                    if (this.upload.type !== 'video') {
                        this.uploading = false;
                        this.output();
                    } else {
                        this.saveSnapshot(filename).then(thumbnail => {
                            this.uploading = false;
                            if (thumbnail === false) {
                                return this.notice('上传出错', 'error');
                            }
                            this.upload.thumbnail = thumbnail;
                            this.output();
                        }).catch(r => {
                            this.uploading = false;
                            this.notice('上传出错', 'error');
                            console.log(r);
                        });
                    }
                }).catch(r => {
                    this.uploading = false;
                    this.notice('上传出错...', 'error');
                    console.log(r)
                })
            },
            //阿里云上传视频
            aliyun_upload(filename) {
                let fullname = this.module + filename + this.upload.suffix,
                    $this = this;
                this.uploading = true;

                this.getOssData().then((data) => {
                    $this.ossClient = $this.getOssClient(data);
                    $this.ossMultiUpload($this.ossClient, fullname, this.file, 0, null, function (progress) {
                        //上传进度
                        $this.dialog.progress = Math.floor(progress * 100);
                        //取消上传
                        if ($this.cancelUpload) {
                            $this.ossClient.cancel();
                        }
                    }, function (r) {
                        //上传完毕 成功
                        if (r.res.statusCode !== 200) {
                            $this.uploading = false;
                            $this.$message.error('上传失败,请联系管理员进行处理');
                        }

                        $this.upload.url = cdnUrl + fullname;

                        if ($this.upload.type !== "video") {
                            $this.uploading = false;
                            $this.output();
                        } else {
                            $this.uploading = false;
                            $this.upload.thumbnail = '';
                            $this.output();
                            //$this.saveSnapshot(filename).then(thumbnail => {
                                //$this.uploading = false;
                                //if (thumbnail === false) {
                                    //return $this.notice('上传出错', 'error');
                                //}
                                //$this.upload.thumbnail = thumbnail;
                                //$this.output();
                            //}).catch(r => {
                                //$this.uploading = false;
                                //$this.notice('上传出错', 'error');
                                //console.log(r);
                            //});
                        }
                    });
                });
            },
            output() {
                this.$nextTick(function () {
                    //重新加载媒体资料
                    if (this.$refs.media) {
                        this.$refs.media.load();
                    }

                    //上传完成后置处理
                    if (this.upload.suffix === '.mp4') {
                        this.upload.url = this.upload.url + '?size=' + this.$refs.video.videoWidth + 'x' + this.$refs.video.videoHeight;
                    } else {
                        this.upload.thumbnail = '';
                    }

                    this.close();
                    this.$emit('input', this.upload);
                });
            },
            //保存缩略图
            saveSnapshot(filename) {
                return axios.post(apiUrl + '/file/upload-base64', {
                    filename: filename + '.jpg',
                    code: this.thumbnail
                }).then((r) => {
                    if (!r.data.ok) {
                        throw new Error(r.data.error)
                    }
                    return r.data.location;
                });
            },
            //删除视频
            remove() {
                if (!this.$refs.media) {
                    return;
                }

                this.progress = '00:00';
                this.duration = '00:00';
                this.$emit('input', {});
            }
        }
    });

    /**
     * 链接 临时替代 el-link
     * @type {string}
     */
    let VULink = `<a :[hrefName]="href" :class="['vu-link', linkUnderline, renderType]" :[targetName]="target">
			<i v-if="icon" :class="icon"></i>
			<slot></slot>
	</a>`;
    Vue.component('vu-link', {
        template: VULink,
        props: {
            href: {
                type: String,
                default: null
            },
            type: {
                type: String,
                default: 'primary'
            },
            target: {
                type: String,
                default: null
            },
            icon: {
                type: String,
                default: ''
            },
            underline: {
                type: Boolean,
                default: false
            }
        },
        computed: {
            hrefName: function () {
                return this.href ? 'href' : null;
            },
            targetName: function () {
                return this.target ? 'target' : null;
            },
            linkUnderline: function () {
                return this.underline ? 'vu-link-underline' : '';
            },
            renderType: function () {
                return 'vu-link-' + this.type;
            }
        }
    });

    let VULocation = `
		<el-button :type="type" :size="size" :icon="icon" @click="header">
		<slot></slot>
</el-button>
	`;
    Vue.component('vu-location', {
        template: VULocation,
        props: {
            href: {
                type: String,
                default: ''
            },
            type: {
                type: String,
                default: ''
            },
            icon: {
                type: String,
                default: ''
            },
            size: {
                type: String,
                default: 'small'
            },
            target: {
                type: String,
                default: ''
            },
            plain: {
                type: Boolean,
                default: false
            },
        },
        methods: {
            header() {
                if (this.target === '_blank') {
                    window.open(this.href);
                } else {
                    window.location.href = this.href;
                }
            }
        }
    });

    let VUButton = `
		<el-button :icon="iconstyle" @click="_click" :type="type" :size="size" :disabled="disabled"><slot></slot></el-button>
	`;
    Vue.component('vu-button', {
        template: VUButton,
        props: {
            size: {
                type: String,
                default: 'small'
            },
            icon: {
                type: String,
                default: ''
            },
            type: {
                type: String,
                default: 'primary'
            }
        },
        computed: {
            iconstyle: function () {
                if (this.loading === false) {
                    return this.icon;
                }
                return 'el-icon-loading';
            }
        },
        data() {
            return {
                loading: false,
                disabled: false
            }
        },
        methods: {
            _click() {
                this.$emit('click');
            }
        }
    });

    Vue.directive('commit', function (el, binding, vnode) {
        vnode.componentInstance.loading = binding.value;
        vnode.componentInstance.disabled = binding.value;
    });

    let ButtonAsync = `<el-button :type="type" :data="data" :size="size" :icon="iconStyle" @click="run" :disabled="loading"><slot></slot></el-button>`;

    Vue.component('ButtonAsync', {
        template: ButtonAsync,
        props: {
            type: {
                type: String,
                default: ''
            },
            icon: {
                type: String,
                default: ''
            },
            size: {
                type: String,
                default: 'small'
            },
            data: {
                default: null
            }
        },
        data() {
            return {
                loading: false
            };
        },
        computed: {
            iconStyle() {
                return this.loading ? 'el-icon-loading' : this.icon;
            }
        },
        methods: {
            run() {
                this.$emit('click', this.callback, this.data);
            },
            callback(fn) {
                if (!fn || !fn.then) {
                    return '';
                }
                this.loading = true;
                fn.then(() => {
                    this.loading = false;
                }).catch(r => {
                    this.loading = false;
                    console.log(r);
                });
            }
        }
    });

    let VUPreview = `<div class="vu-preview">
		<div class="vu-preview-container" v-if="type=='video'" @click="playVideo">
			<img :src="src" />
			<i class="el-icon-video-play"></i>
		</div>
		<el-dialog :title="title" :visible.sync="videoShow" width="25%" :close-on-click-modal="false" :before-close="closeVideo">
			<video :src="mediaUrl" ref="video" controls></video>
		</el-dialog>
		<div class="vu-preview-container" v-if="type=='img'" @click="playImg">
			<img :src="src" />
		</div>
		<el-dialog :title="title" :visible.sync="imgShow" :close-on-click-modal="false">
			<el-carousel height="500px">
			  <el-carousel-item v-for="(val, key) in thumbnail" class="thumbnail" :key="key">
				<img :src="val" alt="">
			  </el-carousel-item>
			</el-carousel>	
		</el-dialog>
	</div>`
    Vue.component('vu-preview', {
        template: VUPreview,
        props: {
            title: {
                type: String,
                default: '预览'
            },
            src: {
                type: String,
                required: true
            },
            mediaUrl: {
                type: String,
                default: ''
            },
            type: {
                type: String,
                default: 'img',
                validator(value) {
                    return ['video', 'img'].indexOf(value) !== -1
                }
            },
            thumbnail: {
                type: Array,
                default() {
                    return []
                }
            }
        },
        data() {
            return {
                videoShow: false,
                imgShow: false
            }
        },
        methods: {
            playVideo() {
                this.videoShow = true
            },
            playImg() {
                this.imgShow = true
            },
            closeVideo(done) {
                this.$refs.video.pause()
                done()
            }
        }
    });

    let VUOpus = `
		<div class="opus-detail">
			<el-button type="primary" size="small" @click="preview">详情</el-button>
			<el-dialog title="作品详情" :close-on-click-modal="false" :visible.sync="showDialog"  :width="type=='video'? '500px' : '700px'" :before-close="close">
				<p v-if="content.length>0">{{content}}</p>
				<ul class="opus-detail-thumbnail" v-if="type=='img'">
					<li v-for="(val,key) in attachment" :key="key" :style="imgSize"><img :src="val.snapshot" alt=""></li>
				</ul>
				<ul class="opus-detail-video" v-if="type=='video'">
					<li v-for="(val,key) in attachment" :key="key"><video :src="val.url" ref="video" controls></video></li>
				</ul>
				<ul v-if="product.length>0">
					<li v-for="(val,key) in product" class="opus-frame">
						<template v-if="productType==1">
							<div class="opus-product-thumbnail"><img :src="val.icon" alt=""></div>
							<div class="opus-product-desc">
								<div>{{val.title}}</div>
							</div>
						</template>
						<template v-if="productType==2">
							<div class="opus-product-thumbnail"><img :src="val.shop_logo" alt=""></div>
							<div class="opus-product-desc"><div>{{val.shop_name}}</div></div>
						</template>
						<template v-if="productType==0">
							<div class="opus-product-thumbnail"><img :src="val.pic" alt=""></div>
							<div class="opus-product-desc">
								<div>{{val.name}}</div>
								<div style="color: #F56C6C">￥{{val.price}}</div>
							</div>
						</template>
					</li>
				</ul>
			</el-dialog>
		</div>
	`
    Vue.component('vu-opus', {
        template: VUOpus,
        props: {
            content: {
                type: String,
                default: ''
            },
            type: {
                type: String,
                default: 'img',
                validator(value) {
                    return ['video', 'img'].indexOf(value) !== -1
                }
            },
            attachment: {
                type: Array,
                default() {
                    return []
                }
            },
            productType: {
                type: Number,
                default: 0
            },
            product: {
                type: Array,
                default() {
                    return []
                }
            }
        },
        data() {
            return {
                showDialog: false
            };
        },
        computed: {
            imgSize() {
                let len = this.attachment.length,
                    size = len >= 3 ? Math.floor(600 / 3) + 'px' : Math.floor(600 / 2) + 'px';

                return `width:${size};height:${size}`;
            }
        },
        methods: {
            preview() {
                this.showDialog = true
            },
            close(done) {
                if (this.type === 'video') {
                    this.$refs.video.forEach((value) => {
                        value.pause();
                    });
                }
                done()
            }
        }
    });

    let VUTable = `
<span>
    <el-button @click="openDialog" :type="type" :size="size">{{name}}</el-button>
    <el-dialog :visible.sync="show" :width="width" :title="title" :close-on-click-modal="false" :append-to-body="appendToBody" :before-close="cancel">
        <vu-action>
            <el-form inline :size="size">
                <slot name="form"></slot>
            </el-form>
        </vu-action>
        <vu-notice type="danger" v-if="limit!==null">当前选择: {{count}} / {{limit}}</vu-notice>
        <slot name="notice"></slot>
        <el-table :data="data" @select="select" @select-all="selectAll" ref="tableData" :height="height" v-loading="loading">
            <el-table-column type="selection"></el-table-column>
            <slot name="body"></slot>
        </el-table>
        <vu-pagination v-model="value.page" :remote-method="fetch"></vu-pagination>
        <span class="dialog-footer" slot="footer">
                <el-button @click="cancel" :size="size">取消</el-button>
                <el-button type="primary" :size="size" :disabled="true" v-if="cached.length==0">确定</el-button>
                <el-button v-else type="primary" :size="size" @click="confirmMethod" :disabled="saving"><i class="el-icon-loading" v-if="saving"></i>确定</el-button>
            </span>
    </el-dialog>
</span>
`

    Vue.component('vu-table', {
        template: VUTable,
        props: {
            value: {
                type: Object,
                default: {
                    checked: [], //被选择的选项
                    page: {
                        total: 0,
                        size: 10,
                        offset: 0
                    }
                }
            },
            title: {
                type: String,
                default: '数据选择'
            },
            type: {
                type: String,
                default: 'primary'
            },
            size: {
                type: String,
                default: 'mini'
            },
            name: {
                type: String,
                default: '请选择...'
            },
            remoteMethod: {
                type: Function,
                required: true
            },
            //分页数据
            data: {
                type: Array,
                required: true
            },
            //数据比较的关键Key
            primaryKey: {
                type: String,
                default: 'id'
            },
            confirm: {
                type: Function
            },
            //选择元素个数限制
            limit: {
                type: Number,
                default: null
            },
            width: {
                type: String,
                default: ''
            },
            height: {
                type: String,
                default: null
            },
            appendToBody: {
                type: [Boolean, String],
                default: false
            }
        },
        data() {
            return {
                show: false,
                loading: false,
                saving: false,
                cached: [],
                requested: false,
                page: {
                    total: 0,
                    size: 10,
                    offset: 0
                },
                backup: []
            }
        },
        computed: {
            count() {
                return this.cached.length;
            }
        },
        methods: {
            init() {
                let that = this;
                Array.prototype.removeElement = function (needle) {
                    this.splice(this.map(r => r[that.primaryKey]).indexOf(needle[that.primaryKey]), 1);
                };

                Array.prototype.hasChildren = function (element) {
                    return this.map(r => r[that.primaryKey]).includes(element[that.primaryKey]);
                };

                this.page = JSON.parse(JSON.stringify(this.value.page))
                this.cached = this.value.checked
                if (parseInt(this.limit) === 1) {
                    this.backup = JSON.parse(JSON.stringify(this.cached))
                }
            },
            fetch() {
                this.$emit('input', this.value)
                this.loading = true
                return this.remoteMethod().then(() => {
                    this.loading = false
                    this.render();
                });
            },
            openDialog() {
                this.init();
                this.show = true;
                this.fetch();
                // if (this.requested === false) {
                //     this.requested = true;
                //
                // }
            },
            //分页渲染数据
            render() {
                this.$nextTick(() => {
                    for (let index in this.data) {
                        this.$refs.tableData.toggleRowSelection(this.data[index], this.cached.hasChildren(this.data[index]))
                    }
                })
            },
            //单选
            select(selection, row) {
                if (selection.hasChildren(row)) {
                    this.addElement(row)
                } else {
                    this.cached.removeElement(row);
                }
            },
            //添加元素
            addElement(value) {
                if (this.limit === null || this.cached.length < this.limit) {
                    !this.cached.hasChildren(value) && this.cached.push(value)
                } else if (parseInt(this.limit) === 1) {
                    this.$refs.tableData.clearSelection();
                    this.$refs.tableData.toggleRowSelection(value, true)
                    this.cached = [];
                    this.cached.push(value);
                } else {
                    this.$refs.tableData.toggleRowSelection(value, false)
                }
            },
            //全选
            selectAll(selection) {
                if (selection.length > 0) {
                    // 全选
                    if (this.limit === null) {
                        selection.forEach(v => this.addElement(v))
                    } else {
                        let distance = this.limit - this.cached.length;
                        this.$refs.tableData.clearSelection();
                        selection = selection.length >= distance ? selection.splice(0, distance) : selection;
                        selection.forEach(v => {
                            this.$refs.tableData.toggleRowSelection(v, true)
                            this.addElement(v)
                        });
                    }

                } else {
                    //取消全选
                    let keeping = [];
                    this.cached.forEach((value) => {
                        if (!this.data.hasChildren(value)) {
                            keeping.push(value);
                        }
                    });

                    this.cached = JSON.parse(JSON.stringify(keeping))
                }
            },
            cancel(done) {
                this.$refs.tableData.clearSelection();
                this.cached = JSON.parse(JSON.stringify(this.backup));
                for (let index in this.data) {
                    this.$refs.tableData.toggleRowSelection(this.data[index], this.cached.hasChildren(this.data[index]))
                }
                if (typeof done === 'function') {
                    done()
                } else {
                    this.show = false
                }
            },
            confirmMethod() {
                this.value.checked = JSON.parse(JSON.stringify(this.cached));
                this.$emit('input', this.value);

                if (typeof this.confirm !== 'function') {
                    this.show = false;
                } else {
                    let fn = this.confirm(this.value.checked);
                    if (!fn || !fn.then) {
                        return;
                    }
                    this.saving = true;
                    fn.then(() => {
                        this.saving = false;
                        this.show = false;
                        this.cached = [];
                    }).catch(r => {
                        this.saving = false;
                        this.show = false;
                        console.log(r);
                    });
                }
            }
        }
    });

    let VUCard = `
	<ul class="vu-card">
		<li v-for="val,key in list" @click="run(val.href)">
			<el-card :class="'vu-card-' + val.type">
				<span class="vu-card-title"><i v-if="val.icon" :class="val.icon"></i>{{val.name}}</span>
				<div class="vu-card-desc">{{val.desc}}</div>
			</el-card>
		</li>
    </ul>
	`;
    Vue.component('vu-card', {
        template: VUCard,
        props: {
            data: { //每一组包含属性 name theme icon desc
                type: Array,
                default: []
            }
        },
        data() {
            return {
                list: []
            }
        },
        created() {
            this.init();
        },
        methods: {
            init() {
                for (let index = 0, len = this.data.length; index < len; ++index) {
                    var item = this.data[index],
                        type = item.hasOwnProperty('type') && item.type ? item.type : 'primary',
                        icon = item.hasOwnProperty('icon') ? item.icon : 'el-icon-setting',
                        desc = item.hasOwnProperty('desc') ? item.desc : '',
                        href = item.hasOwnProperty('href') ? item.href : '';

                    this.list.push({
                        name: item.name,
                        type: type,
                        icon: icon,
                        desc: desc,
                        href: href
                    });
                }
            },
            run(href) {
                if (href === '') {
                    return;
                }
                window.location.href = href;
            }
        }
    });

    let ButtonPrompt = `
        <span class="button-prompt">
            <el-button :size="size" :type="type" @click="show=true" :icon="loading ? 'el-icon-loading' : icon" :disabled="loading || disabled">{{name}}</el-button>
            <el-dialog :title="title" :visible.sync="show" :close-on-click-modal="false" :width="width" :before-close="_close" :append-to-body="appendToBody">
                <slot></slot>
                <span class="dialog-footer" slot="footer">
                    <el-button :size="size" @click="_close">取消</el-button>
                    <slot name="footer">
                        <el-button :size="size" type="primary" @click="_confirm" :disabled="committing"><i class="el-icon-loading" v-if="committing"></i>确认</el-button>
                    </slot>
                </span>
            </el-dialog>
        </span>
    `;
    Vue.component('button-prompt', {
        template: ButtonPrompt,
        props: {
            name: {
                required: true,
                type: String,
                default: ''
            },
            size: {
                type: String,
                default: 'small'
            },
            icon: {
                type: String,
                default: ''
            },
            title: {
                type: String,
                default: '警告'
            },
            width: {
                type: String,
                default: '25%'
            },
            type: {
                type: String,
                default: 'plain',
                validator(value) {
                    return ['text', 'primary', 'success', 'warning', 'danger', 'info', 'plain'].indexOf(value) !== -1;
                }
            },
            beforeClose: {
                type: Function,
                default: null
            },
            data: null,
            confirm: {
                type: Function,
                default: null
            },
            appendToBody: {
                type: Boolean,
                default: false
            },
            disabled: {
                type: Boolean,
                default: false
            }
        },
        data() {
            return {
                show: false,
                loading: false,
                committing: false
            }
        },
        methods: {
            _close() {
                typeof this.beforeClose === 'function' && this.beforeClose();
                this.show = false;
            },
            _confirm() {
                this.$emit('click', this.callback, this.data);
            },
            callback(fn) {
                if (!fn) {
                    return;
                }
                if (typeof this.beforeClose === 'function') {
                    fn.then(() => {
                        this.committing = false;
                        this.show = false;
                    }).catch(r => {
                        console.log(r)
                    });
                } else {
                    this.show = false;
                    this.loading = true;
                    fn.then(r => {
                        this.loading = false;
                        console.log(r)
                    }).catch(r => {
                        this.loading = false;
                        console.log(r)
                    });
                }
            }
        }
    });

    //按钮组
    Vue.component('button-groups', {
        render(createElement) {
            function deepClone(vnodes, createElement) {
                function cloneVNode(vnode) {
                    const clonedChildren = vnode.children && vnode.children.map(vnode => cloneVNode(vnode));
                    const cloned = createElement(vnode.tag, vnode.data, clonedChildren);
                    cloned.text = vnode.text;
                    cloned.isComment = vnode.isComment;
                    cloned.componentOptions = vnode.componentOptions;
                    cloned.elm = vnode.elm;
                    cloned.context = vnode.context;
                    cloned.ns = vnode.ns;
                    cloned.isStatic = vnode.isStatic;
                    cloned.key = vnode.key;
                    return cloned;
                }

                return vnodes.map(vnode => cloneVNode(vnode))
            }

            let mainly = this.slots.slice(0, this.number),
                more = this.slots.slice(this.number);

            return createElement('div', {
                class: 'vu-button-groups',
                key: Math.floor((new Date()).getTime() / 1000)
            }, [
                ...deepClone(mainly, createElement),
                createElement('ElTooltip', {
                    attrs: {
                        placement: 'bottom',
                        effect: 'light',
                        'popper-class': 'vu-button-groups-tooltip'
                    }
                }, (this.length > this.number ? [
                    createElement('div', {
                        class: 'vu-button-groups-more',
                        key: Math.floor((new Date()).getTime() / 1000),
                        slot: 'content'
                    }, [...deepClone(more, createElement)]),
                    createElement('ElLink', ['更多', createElement('i', {class: 'el-icon-arrow-down'})])
                ] : []))
            ]);
        },
        props: {
            number: {
                type: Number,
                default: 4
            }
        },
        data() {
            return {
                slots: [],
                length: 0
            }
        },
        beforeUpdate() {
            this.init();
        },
        created() {
            this.init()
        },
        methods: {
            init() {
                this.slots = this.$slots.default.filter(v => v.tag !== undefined)
                this.length = this.slots.length;
            }
        }
    });

    let NumberInput = `<el-input @input="inputVal" @blur="blurVal" v-model="val"></el-input>`;
    Vue.component('number-input', {
        template: NumberInput,
        props: {
            value: null,
            size: {
                type: String,
                default: 'small'
            },
            scale: {
                type: Number,
                default: null
            },
            negative: {
                type: Boolean,
                default: false
            },
            min: {
                type: Number,
                default: null,
            },
            max: {
                type: Number,
                default: null
            }
        },
        data() {
            return {
                val: ''
            }
        },
        created() {
            this.init();
        },
        methods: {
            init() {
                this.val = this.value;
            },
            inputVal() {
                let negativeNumber = /^-$/,
                    integer = /^-?(?:0|[1-9]\d*)$/,
                    prefix = /^-?(?:0|[1-9]\d*)\.$/,
                    float = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/;

                this.val = this.val.toString();

                console.log(this.negative && negativeNumber.test(this.val), integer.test(this.val), prefix.test(this.val), float.test(this.val))
                if (!((this.negative && negativeNumber.test(this.val)) || integer.test(this.val) || prefix.test(this.val) || float.test(this.val))) {
                    this.val = this.val.substring(0, this.val.length - 1);
                }

                if (this.scale !== null) {
                    let index = this.val.indexOf('.');
                    this.val = index !== -1 ? this.val.substring(0, index + this.scale + 1) : this.val;
                }

                this.$emit('input', this.val);
            },
            blurVal() {
                let reg = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/
                if (!reg.test(this.val)) {
                    this.val = '';
                }
                this.$emit('input', this.val);
            }
        },
    });

    let ButtonBack = `<el-button :size="size" @click="back"><slot>取消</slot></el-button>`;
    Vue.component('button-back', {
        template: ButtonBack,
        props: {
            size: {
                type: String,
                default: 'small'
            }
        },
        methods: {
            back() {
                history.go(-1)
            }
        }
    });

    let VuTextarea = `<div :class="selector" :id="editorId"></div>`;
    Vue.component('vu-textarea', {
        template: VuTextarea,
        props: {
            value: String,
            width: {
                type: Number,
                default: 800,
            },
            height: {
                type: Number,
                default: 400
            },
            placeholder: {
                type: String,
                default: null
            }
        },
        data() {
            return {
                selector: 'tinymce',
                setting: {
                    width: 0,
                    height: 0,
                    forced_root_block: '',
                    element_format: 'html',
                    image_dimensions: false,
                    extended_valid_elements: "*[*]",
                    inline_styles: true,
                    keep_styles: true,
                    valid_children: "+body[style]",
                    images_upload_url: apiUrl + "/file/upload",
                    valid_elements: '*[*]',
                    language: "zh_CN",
                    menubar: false,
                    images_upload_credentials: true,
                    toolbar: 'undo redo | styleselect | bold italic strikethrough forecolor backcolor | link image | alignleft aligncenter alignright | preview code ',
                    content_css: [
                        "../../assets/css/article/collection.css",
                    ],
                    plugins: [
                        'advlist autolink lists link image charmap print preview anchor textcolor ' +
                        'searchreplace visualblocks code fullscreen insertdatetime media table contextmenu paste ' +
                        'code help placeholder'
                    ],
                    init_instance_callback: function (editor) {
                        editor.on('SetContent', function (e) {
                            let E = e.target.$;
                            if (E('.video_iframe').length != 0) {
                                app.dealVideo(E);
                            }
                        });
                    },
                    images_upload_handler: function (blobInfo, success, failure) {
                        let form = new FormData();
                        form.append('file', blobInfo.blob(), blobInfo.filename());
                        $.ajax({
                            url: apiUrl + "/file/upload",
                            type: "post",
                            data: form,
                            processData: false,
                            contentType: false,
                            success: function (data) {
                                success(data.location + '?x-oss-process=image/auto-orient,1');
                            },
                            error: function (e) {
                                alert("图片上传失败");
                            }
                        });
                    }
                },
                data: '',
            }
        },
        created() {
            this.setting.width = this.width;
            this.setting.height = this.height;
        },
        computed: {
            editorId() {
                return 'tinymce-' + (new Date()).getTime();
            }
        },
        mounted() {
            this.data = this.value;
            this.init();
        },
        watch: {
            data(n) {
                this.$emit('input', n);
            }
        },
        methods: {
            init() {
                if (typeof tinymce === 'undefined') {
                    throw new Error('please install the tinymce');
                }
                tinymce.init(Object.assign(this.setting, {
                    selector: '#' + this.editorId,
                    setup: (editor) => {
                        editor.on('init', () => {
                            editor.setContent(this.data);
                            //fix execCommand not change ,more see issues#2
                            editor.on('input change undo redo execCommand KeyUp', () => {
                                this.data = editor.getContent();
                                // if (this.status === INPUT || this.status === INIT) return this.status = CHANGED;
                            });
                            //fix have chang not to emit input,more see issues #4
                            editor.on('NodeChange', () => {
                                this.data = editor.getContent();
                            });
                        });
                    }
                }))
            }
        }
    });

    let LinkPrompt = `<span>
<el-link :type="type" @click="show=true">{{label}}</el-link>
<el-dialog :title="title" :visible.sync="show" :before-close="close" append-to-body :close-on-click-modal="false" :width="width">
    <slot></slot>
    <span slot="footer">
    <el-button @click="close" size="small">取消</el-button>
    <el-button type="primary" size="small" @click="confirm" :icon="iconStyle" :disabled="loading">确认</el-button>
</span>
</el-dialog>
</span>`
    Vue.component('link-prompt', {
        template: LinkPrompt,
        props: {
            label: {
                type: String,
                required: true
            },
            data: [Object, Number, String, null],
            type: {
                type: String,
                default: 'primary',
                validator(value) {
                    return ['primary', 'danger', 'warning', 'success', 'info'].includes(value);
                }
            },
            desc: {
                type: String,
                default: '警告',
            },
            width: {
                type: String,
                default: '400px'
            }
        },
        data() {
            return {
                show: false,
                loading: false
            }
        },
        computed: {
            iconStyle() {
                return this.loading ? 'el-icon-loading' : '';
            },
            title() {
                return this.desc ? this.desc : this.label;
            }
        },
        methods: {
            close() {
                this.show = false;
            },
            confirm() {
                if (typeof this.$listeners.confirm === 'function') {
                    this.$emit('confirm', {Promise: this.callback}, this.data)
                }
            },
            callback(fn) {
                if (!fn || !fn.then) {
                    return
                }
                this.loading = true;
                fn.then(r => {
                    this.loading = false;
                    this.close()
                }).catch(r => {
                    this.loading = false;
                })
            }
        }
    })

    /**
     * 商品
     * @type {string}
     */
    let Goods = `<el-popover v-model="visible" placement="top" trigger="manual">
        <div class="tk-goods">
            <div class="tk-sku" v-for="(spec, key) in data.specs" :key="key">
                <div class="sku-title">{{spec.title}}</div>
                <ul class="sku-attributes">
                    <li v-for="(sku, k) in spec.value"
                        @click="choose(spec.id, sku.id, key, k)"
                        :class="[sku.active ? '' : 'unavailable', subIndex[key] == k ? 'active' : '']">{{ sku.value }}
                    </li>
                </ul>
            </div>
            <vu-notice type="danger">选择的商品：{{ cached }} <template v-if="storage">(库存：{{ storage }})</template></vu-notice>
            <div class="goods-footer">
                <el-button @click="cancel" size="small">取消</el-button>
                <el-button type="primary" @click="confirm" :disabled="disabled" size="small">确认</el-button>
            </div>
        </div>
        <vu-notice type="primary" slot="reference">
            <template v-if="cached">
                <span>选择的商品：{{ cached }} <template v-if="storage">(库存：{{ storage }})</template></span>
                <el-link :underline="false" @click="visible=true">修改</el-link>
            </template>
            <template v-else>
                <el-link type="primary" :underline="false" @click="visible=true">请选择商品规格</el-link>
            </template>
        </vu-notice>
    </el-popover>`;
    Vue.component('tk-goods', {
        template: Goods,
        props: {
            value: [Number, String],
            sku: {
                type: Array,
                required: true
            },
            specs: {
                type: Array,
                required: true
            }
        },
        data() {
            return {
                visible: false,
                swap: {}, //存放要和选中的值进行匹配的数据
                data: {
                    specs: [], //规格数据
                    sku: [] // 商品数据
                },
                selected: [], //选择的规格选项
                subIndex: [],
                sku_id: 0,
                storage: 0,
                //数据备份
                copy: {
                    specs: [],
                    sku: [],
                    sku_id: 0
                }
            };
        },
        created() {
            this.beforeStartup();
            this.backup();
            this.startup(this.value);
        },
        watch: {
            value() {
                this.beforeStartup();
                this.startup(this.value)
            }
        },
        computed: {
            disabled() {
                let selected = JSON.parse(JSON.stringify(this.selected));
                selected = selected.filter((val) => val !== '');

                this.storage = 0;

                return selected.length !== this.specs.length;
            },
            cached() {
                let cached = [],
                    storage = 0;
                this.specs.forEach((spec) => {
                    spec.value.forEach((val) => {
                        if (this.selected.includes(spec.id + '_' + val.id)) {
                            cached.push(val.value);
                        }
                    });
                })
                return cached.join(' ');
            },
        },
        methods: {
            startup(goods_id) {
                this.selected = [];
                let selected = this.fetchGoods(goods_id);
                selected.length == 0 ? this.active() : this.setColor(selected);
            },
            setColor(selected) {
                this.data.specs.forEach((spec, spec_key) => {
                    spec.value.forEach((sku, sku_key) => {
                        let key = spec.id + '_' + sku.id;
                        if (selected.includes(key)) {
                            this.choose(spec.id, sku.id, spec_key, sku_key);
                        }
                    });
                });
            },
            backup() {
                this.copy.specs = JSON.parse(JSON.stringify(this.data.specs));
                this.copy.sku = JSON.parse(JSON.stringify(this.data.sku));
                let sku_id = this.value;
                this.copy.sku_id = sku_id;
            },
            beforeStartup() {
                this.data.specs = JSON.parse(JSON.stringify(this.specs));
                this.data.sku = JSON.parse(JSON.stringify(this.sku));
                this.data.sku.forEach((sku) => {
                    let values = JSON.parse(JSON.stringify(sku.values));
                    values.sort();
                    sku.values = values.join(',');
                    this.$set(this.swap, sku.values, sku);
                });
            },
            swapping() {
                this.swap = Object.assign({}, {});
                this.$set(this.data, 'specs', JSON.parse(JSON.stringify(this.copy.specs)));
                this.$set(this.data, 'sku', JSON.parse(JSON.stringify(this.copy.sku)));
                this.data.sku.forEach((sku) => {
                    this.$set(this.swap, sku.values, sku);
                });
            },
            fetchGoods(goods_id) {
                if (!goods_id) {
                    return [];
                }
                for (let spu of this.data.sku) {
                    if (spu.goods_no == goods_id) {
                        return spu.values.split(',');
                    }
                }
                return [];
            },
            active() {
                //定义数组储存被选中的值
                let selected = [];

                this.data.specs.forEach((spec, index) => {
                    selected[index] = this.selected[index] ? this.selected[index] : '';
                });

                this.data.specs.forEach((spec, index) => {
                    let swap = selected[index];
                    spec.value.forEach((item, key) => {
                        selected[index] = spec.id + '_' + item.id;
                        item.active = this.checked(selected);
                    });
                    selected[index] = swap;
                });

                this.query();
            },
            query() {
                if (this.disabled) {
                    return;
                }
                let sku = JSON.parse(JSON.stringify(this.selected));

                sku.sort();
                sku = this.swap[sku];
                this.sku_id = sku ? sku.goods_no : '';
                this.storage = sku ? sku.storage : '';
            },
            checked(result) {
                for (var i in result) {
                    if (result[i] == '') {
                        return true; //如果数组里有为空的值，那直接返回true
                    }
                }

                let key = JSON.parse(JSON.stringify(result));
                key.sort();
                if (typeof this.swap[key] === 'undefined') {
                    return false;
                }

                //匹配选中的数据的库存，若不为空返回true反之返回false
                return this.swap[key].storage == 0 ? false : true;
            },
            choose(spec_id, sku_id, n, index) {
                let item = spec_id + '_' + sku_id;
                if (this.selected[n] != item) {
                    this.$set(this.selected, n, item);
                    this.$set(this.subIndex, n, index)
                } else {
                    this.$set(this.selected, n, "");
                    //去掉选中的颜色
                    this.$set(this.subIndex, n, -1);
                }
                this.active();
            },
            cancel() {
                this.visible = false;
                this.$emit('input', this.copy.sku_id);
                this.swapping();
                this.startup(this.copy.sku_id);
            },
            confirm() {
                this.visible = false;
                this.$emit('input', this.sku_id);
            }
        }
    });

    let PlayIcon = `<div class="audio-icon" @click="$emit('click')">
        <div class="column" style="animation-delay: -1.2s;"></div>
        <div class="column"></div>
        <div class="column" style="animation-delay: -1.5s;"></div>
        <div class="column" style="animation-delay: -0.9s;"></div>
        <div class="column" style="animation-delay: -0.6s;"></div>
    </div>`;
    Vue.component('play-icon', {
        template: PlayIcon,
        methods: {
            click() {
                this.$emit('click')
            }
        }
    });

    let Weixin = `
<div class="tk-gzh">
    <ul class="gzh-tag" v-if="tagList.length>0">
    <li v-for="v,k in tagList" :key="k">{{v.label}}：<span class="gzh-code"><template>{$</template>{{v.name}}<template>}</template></span></li>
</ul>
<div class="content" contentEditable="true" v-show="false" @focus="focus" ref="context" @input="input"></div>
</div>
`;
    Vue.component('tk-gzh', {
        template: Weixin,
        props: {
            value: String,
            tagList:{
                type: Array,
                default: []
            }
        },
        data() {
            return {
            }
        },
        methods: {
            insert(context) {
                let content = '{$'+context+'}';
                this.$refs.context.focus();
                let range = window.getSelection().getRangeAt(0);
                range.collapse(false);
                console.log(range)
                let node = range.createContextualFragment(content);
                let child = node.lastChild;
                range.insertNode(node);
                if (child) {
                    range.setEndAfter(child);
                    range.setStartAfter(child)
                }
                let selection = window.getSelection();
                selection.removeAllRanges();
                selection.addRange(range);
                // this.$refs.context.focus();
            },
            input() {
                console.log(window.getSelection().getRangeAt(0))
                console.log(this.$refs.context.innerHTML)
                this.$refs.context.focus();
            },
            focus() {
                console.log(window.getSelection().getRangeAt(0))
            }
        }
    })
})();
